<?php

namespace gateim\library\server;

use gateim\Config;
use gateim\library\action\WsConnect;
use gateim\library\tool\Validator;
use Swoole\Coroutine;
use Swoole\Database\RedisConfig;
use Swoole\Database\RedisPool;
use Swoole\Runtime;

/*
 * webSocket 服务
*/
class Swooles
{
    use Config;
    public $server; // websocket 服务实例
    public $validator; // 验证数据类
    public $redisPool; // redis 连接池

    // websocket 服务器
    public function __construct(){
        // 检查是否配置好ip与端口
        $ip = self::$websocket['ServiceHost'];
        $port = self::$websocket['ServicePort'];
        if(empty($ip) ||  empty($port)){
            $title = "swoole 服务启动失败";
            $msg = "请在配置文件中配置好 swoole 的 ip与端口,并保证端口已开放".PHP_EOL;
            $msg .= "配置文件地址：根目录/application/extra/swoole.php".PHP_EOL;
            $this -> ConsoleTips($title,$msg);
            exit();
        }

        //创建WebSocket Server对象，监听0.0.0.0:9502端口
        try {
            if(self::$server_config['openSll']){
                $is_Sll = SWOOLE_SOCK_TCP | SWOOLE_SSL;
            }else{
                $is_Sll = SWOOLE_SOCK_TCP;
            }
            unset(self::$server_config['openSll']);
            $this -> server = new \Swoole\WebSocket\Server($ip, $port, SWOOLE_PROCESS, $is_Sll);
        } catch (\Exception $e) {
            $title = "swoole 服务启动失败";
            $msg = "请检查下方操作是否正确".PHP_EOL;
            $msg .= "1、swoole 是否已安装".PHP_EOL;
            $msg .= "2、配置信息是否已填写".PHP_EOL;
            $msg .= "3、{$port}端口是否打开".PHP_EOL;
            $msg .= "4、{$port}端口是否被占用".PHP_EOL;
            $msg .= "5、如果开启了sll证书，请检查证书路径是否填写".PHP_EOL;
            $this -> ConsoleTips($title,$msg);
            exit();
        }

        // 首次加载时，清空相关的 redis 数据
        (new WsConnect()) -> emptyRedis();

        $this -> validator = new Validator();

        // 配置信息
        $this -> server -> set(self::$server_config);

        // 监听各类事件
        $this -> server -> on('Open',[$this,'onOpen']);
        $this -> server -> on('request',[$this,'onRequest']);
        $this -> server -> on('Message',[$this,'onMessage']);
        $this -> server -> on('Task',[$this,'onTask']);
        $this -> server -> on('Finish',[$this,'onFinish']);
        $this -> server -> on('Close',[$this,'onClose']);

        // 开启服务
        $daemonize = self::$server_config['daemonize'];
        $daemonize = $daemonize?'守护进程':'开发模式';

        $title = "swoole 服务启动成功";
        $msg = "ip:".$ip.'   ';
        $msg .= "port:".$port.PHP_EOL;
        $msg .= "运行模式：".$daemonize.PHP_EOL;
        $msg .= "温馨提示：无法连接时，大概率为端口 {$port} 未开放".PHP_EOL;
        $this -> ConsoleTips($title,$msg);

        Runtime::enableCoroutine();
        $this -> server -> start();


    }

    //监听WebSocket连接打开事件
    public function onOpen($ws, $request){
        // 将fd与uid绑定
        $uid = $request -> get['uid'] ?? 0;
        $wsConnect = new WsConnect($this -> server,$this -> getRedis());
        $wsConnect -> bind_fd_uid($request->fd,$uid);
        $this -> redisPool->put($this -> getRedis());
    }

    // 监听http请求
    public function onRequest($request, $response){
        // 验证并处理数据
        list($type,$action,$data) = $this -> validator -> valid($request -> post['type'],$request -> post['action'],$request -> post['data']);
        if(isset($type['code'])){
            // 数据格式错误，通知客户端
            $response->end(json_encode($type));
        }


        switch ($type){
            // im通信
            case 'im':
                // 获取参数
                $val1 = '';
                $val2 = '';
                if(count($this -> validator -> scene[$action]) > 0){
                    $val1 = $data[$this -> validator -> scene[$action][0]];

                }
                if(count($this -> validator -> scene[$action]) > 1){
                    $val2 = $data[$this -> validator -> scene[$action][1]];
                }

                // 执行方法
                $res['code'] = 1;
                $res['msg'] = '执行成功';
                $wsConnect = new WsConnect($this -> server,$this -> getRedis());
                $res['data'] = $wsConnect -> $action($val1,$val2);
                $this -> redisPool->put($this -> getRedis());

                $response->end(json_encode($res));
                break;
            case 'task':
                // task 异步操作

                break;
        }

    }

    //监听WebSocket消息事件
    public function onMessage($ws, $frame){
        $wsConnect = new WsConnect($this -> server,$this -> getRedis());
        $wsConnect -> sendUid('15625559428',$frame->data);
        $this -> redisPool->put($this -> getRedis());
    }

    //监听WebSocket连接关闭事件
    public function onClose($ws, $fd){
        $wsConnect = new WsConnect($this -> server,$this -> getRedis());
        $wsConnect -> delFd($fd);
        $this -> redisPool->put($this -> getRedis());
    }

    //处理异步任务(此回调函数在task进程中执行)
    public function onTask($serv, $task_id, $reactor_id, $data){


        //返回任务执行的结果
        $serv->finish('task');
    }

    // redis 连接池,返回当前连接对象
    public function getRedis(){
        $this -> redisPool = new RedisPool((new RedisConfig)
            ->withHost($this->redis_config['host'])
            ->withPort($this->redis_config['port'])
            ->withAuth($this->redis_config['password'])
            ->withDbIndex($this->redis_config['select'])
            ->withTimeout($this->redis_config['timeout'])
        );
        $redis = $this -> redisPool->get();
        return $redis;
    }

    //处理异步任务的结果(此回调函数在worker进程中执行)
    public function onFinish($serv, $task_id, $data){}

    // 提示
    public function ConsoleTips($title,$message){
        echo PHP_EOL;
        echo "*************************** {$title} ***************************".PHP_EOL.PHP_EOL;
        echo $message.PHP_EOL;
        echo "********************************** 分割线 *********************************".PHP_EOL.PHP_EOL;
    }
}
